package com.ocean.admin.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ocean.admin.entity.SysRole;
import com.ocean.admin.entity.SysRoleDept;
import com.ocean.admin.entity.SysRoleMenu;
import com.ocean.admin.entity.SysUserRole;
import com.ocean.admin.mapper.SysRoleMapper;
import com.ocean.admin.model.dto.rule.SysRoleQueryDTO;
import com.ocean.admin.model.dto.rule.SysRoleSaveDTO;
import com.ocean.admin.model.dto.rule.SysRoleUpdateDTO;
import com.ocean.admin.model.vo.menu.SysMenuRuleVo;
import com.ocean.admin.model.vo.role.SysRoleDetailVo;
import com.ocean.admin.model.vo.role.SysRoleVo;
import com.ocean.admin.extension.security.core.SysUserDetails;
import com.ocean.admin.extension.security.filter.WebFilterExtend;
import com.ocean.admin.service.*;
import com.ocean.core.exception.verify.ApplicationException;
import com.ocean.core.util.constants.ConstantsAttribute;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 角色信息表 服务实现类
 * </p>
 *
 * @author zhang
 * @since 2022-08-16
 */
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements ISysRoleService {

    @Autowired
    private ISysUserRoleService iSysUserRoleService;

    @Autowired
    private ISysRoleMenuService iSysRoleMenuService;

    @Autowired
    private ISysRoleDeptService iSysRoleDeptService;

    @Autowired
    private ISysMenuService iSysMenuService;

    /**
     * 根据角色ID查询已有的菜单权限ID
     *
     * @param roleId            角色ID
     * @param menuCheckStrictly 菜单树选择项是否关联显示
     * @return 选中菜单列表
     */
    @Override
    public List<Long> selectMenuListByRoleId(Long roleId, Boolean menuCheckStrictly) {
        return baseMapper.selectMenuListByRoleId(roleId, menuCheckStrictly);
    }

    /**
     * 分页查询角色列表
     *
     * @param queryDTO 查询条件
     * @return 结果
     */
    @Override
    public IPage<SysRoleVo> pageList(SysRoleQueryDTO queryDTO) {
        Page<SysRoleVo> page = new Page<>(queryDTO.getPage(), queryDTO.getPageSize());
        IPage<SysRoleVo> pageList = baseMapper.pageList(page, queryDTO);
        pageList.getRecords().forEach(res -> res.setStatusName(res.getStatus() == 1 ? "停用" : "正常"));
        return pageList;
    }

    /**
     * 新增角色
     *
     * @param saveDTO 角色信息
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String saveRule(SysRoleSaveDTO saveDTO) {
        this.checkSaveRole(saveDTO);
        SysRole role = new SysRole();
        BeanUtils.copyProperties(saveDTO, role);
        SysUserDetails current = WebFilterExtend.current();
        role.setCreateUserId(current.getUserId());
        role.setCreateUserName(current.getNickName());
        role.setUpdateUserId(current.getUserId());
        role.setUpdateUserName(current.getNickName());
        this.save(role);
        this.saveRoleMenu(saveDTO.getMenuIds(), role);
        return ConstantsAttribute.INSERT_SUCCESS;
    }

    /**
     * 删除角色
     *
     * @param roleIds 角色ID
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String removeRole(Long[] roleIds) {
        List<Long> roleList = Arrays.asList(roleIds);
        this.checkRemoveRole(roleList);
        // 删除角色与菜单关联
        iSysRoleMenuService.remove(Wrappers.<SysRoleMenu>lambdaQuery()
                .in(SysRoleMenu::getRoleId, roleList));
        // 删除角色与部门关联
        iSysRoleDeptService.remove(Wrappers.<SysRoleDept>lambdaQuery()
                .in(SysRoleDept::getRoleId, roleList));
        this.removeByIds(roleList);
        return ConstantsAttribute.REMOVE_SUCCESS;
    }

    /**
     * 修改角色
     *
     * @param updateDTO 角色信息
     * @return 结果
     */
    @Override
    public String updateRole(SysRoleUpdateDTO updateDTO) {
        this.checkUpdateRole(updateDTO);
        SysRole role = new SysRole();
        BeanUtils.copyProperties(updateDTO, role);
        SysUserDetails current = WebFilterExtend.current();
        role.setUpdateUserName(current.getNickName());
        role.setUpdateUserId(current.getUserId());
        this.updateById(role);
        //先删除绑定的菜单信息，然后再添加
        iSysRoleMenuService.remove(Wrappers.<SysRoleMenu>lambdaQuery()
                .eq(SysRoleMenu::getRoleId, role.getRoleId()));
        this.saveRoleMenu(updateDTO.getMenuIds(), role);
        return ConstantsAttribute.UPDATE_SUCCESS;
    }

    /**
     * 根据用户ID查询角色权限
     *
     * @param userId 用户ID
     * @return 角色
     */
    @Override
    public Set<String> selectRolesByUserId(Long userId) {
        List<SysRole> perms = baseMapper.selectRolePermissionByUserId(userId);
        Set<String> permsSet = new HashSet<>();
        for (SysRole perm : perms) {
            if (perm != null) {
                permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(",")));
            }
        }
        return permsSet;
    }

    /**
     * 修改角色状态
     *
     * @param roleId 角色ID
     * @param status 角色状态（0正常 1停用）
     * @return 结果
     */
    @Override
    public String updateRoleStatus(Long roleId, Integer status) {
        baseMapper.update(null, Wrappers.<SysRole>lambdaUpdate()
                .set(SysRole::getStatus, status).eq(SysRole::getRoleId, roleId));
        return ConstantsAttribute.UPDATE_SUCCESS;
    }

    /**
     * 查询角色列表
     *
     * @return 结果
     */
    @Override
    public List<SysRole> roleList() {
        return baseMapper.selectList(null);
    }

    /**
     * 查询角色详情
     *
     * @param roleId 角色ID
     * @return 结果
     */
    @Override
    public SysRoleDetailVo roleInfo(Long roleId) {
        SysRoleDetailVo detailVo = new SysRoleDetailVo();
        SysMenuRuleVo sysMenuRuleVo = iSysMenuService.roleMenuTreeSelect(roleId);
        detailVo.setMenuRuleVo(sysMenuRuleVo);
        return detailVo;
    }

    private void checkUpdateRole(SysRoleUpdateDTO updateDTO) {
        SysRole nameRole = baseMapper.selectOne(Wrappers.<SysRole>lambdaQuery()
                .eq(SysRole::getRoleName, updateDTO.getRoleName()));
        if (nameRole != null && !Objects.equals(nameRole.getRoleId(), updateDTO.getRoleId())) {
            throw new ApplicationException("角色名已存在，请更换");
        }
        SysRole keyRole = baseMapper.selectOne(Wrappers.<SysRole>lambdaQuery()
                .eq(SysRole::getRoleKey, updateDTO.getRoleKey()));
        if (keyRole != null && !Objects.equals(keyRole.getRoleKey(), updateDTO.getRoleKey())) {
            throw new ApplicationException("角色权限字符已存在，请更换");
        }
    }

    private void checkRemoveRole(List<Long> roleList) {
        long count = iSysUserRoleService.count(Wrappers.<SysUserRole>lambdaQuery()
                .in(SysUserRole::getUserId, roleList));
        if (count > 0) {
            throw new ApplicationException("所选中的角色中含有被分配的角色，不能删除");
        }
    }

    private void checkSaveRole(SysRoleSaveDTO saveDTO) {
        Long nameCount = baseMapper.selectCount(Wrappers.<SysRole>lambdaQuery().eq(SysRole::getRoleName, saveDTO.getRoleName()));
        if (nameCount > 0) {
            throw new ApplicationException("角色名已存在，请更换");
        }
        Long keyCount = baseMapper.selectCount(Wrappers.<SysRole>lambdaQuery().eq(SysRole::getRoleKey,
                saveDTO.getRoleKey()));
        if (keyCount > 0) {
            throw new ApplicationException("角色权限字符已存在，请更换");
        }
    }

    private void saveRoleMenu(Long[] menuIds, SysRole role) {
        //新增角色菜单
        if (menuIds != null && menuIds.length > 0) {
            List<SysRoleMenu> collect = Arrays.stream(menuIds).map(res -> {
                SysRoleMenu menu = new SysRoleMenu();
                menu.setRoleId(role.getRoleId());
                menu.setMenuId(res);
                return menu;
            }).collect(Collectors.toList());
            iSysRoleMenuService.saveBatch(collect);
        }
    }
}
